package cl.regalloc.graph;

import java.util.HashMap;
import java.util.List;


/**
 * Data Abstraction of a Node
 * Node contains information about its colour, links to neighbour nodes
 * as well as cardinality information for MCS.
 * @author andyli
 *
 */

public class Node implements Cloneable {

	public final static int UNCOLOURED = -1;

	public final static int MCS_UNVISITED = 0;
	public final static int MCS_VISITED = 1;
	public final static int MCS_SPILT = 2;

	private String nodeKey;
	private HashMap<String, Node> succs;
	private HashMap<String, Node> prdcs;

	private int colour;
	private static Node[] assignedColourMap;
	private List<Node> coalescedNodesList;

	private int cardinality;
	private int mcsStatus;		// for MCS search

	public Node(String nodeKey) {
		this.nodeKey = nodeKey;
		this.colour = UNCOLOURED;
		this.succs = new HashMap<String, Node>();
		this.prdcs = new HashMap<String, Node>();

		this.cardinality = 0;
		this.mcsStatus = MCS_UNVISITED;
	}

	public void setMcsStatus(int status) {
		if ((status == MCS_VISITED) || (status == MCS_SPILT))
			this.mcsStatus = status;
		else
			this.mcsStatus = MCS_UNVISITED;
	}

	public int getMcsStatus() {
		return this.mcsStatus;
	}

	public static void setNumColours(int numColours) {
		assignedColourMap = new Node[numColours];
	}

	public void resetCardinality() {
		this.cardinality = 0;
	}


	public void incCardinality() {
		this.cardinality++;
	}

	public int getCardinality() {
		return this.cardinality;
	}


	public int getColour() {
		return this.colour;
	}


	public void addToCoalescedNodes(Node node) {
		this.coalescedNodesList.add(node);
	}


	public List<Node> getCoalescedNodesList() {
		return coalescedNodesList;
	}


	public void setColour(int colour) {
		this.colour = colour;
	}


	public int assignColour() {
		for(int i = 0; i < assignedColourMap.length; i++)
			assignedColourMap[i] = null;
		this.markUsedColours(this.prdcs);
		this.markUsedColours(this.succs);
		for(int i = 0; i < assignedColourMap.length; i++)
			if(assignedColourMap[i] == null) {
				this.colour = i;
				break;
			}
		return this.colour;
	}

	private void markUsedColours(HashMap<String, Node> map) {
		for (String currentKey : map.keySet()) {
			if(map.get(currentKey).getColour() != UNCOLOURED)
				assignedColourMap[map.get(currentKey).getColour()] = map.get(currentKey);
		}
	}

	public String getNodeKey() {
		return nodeKey;
	}

	public void setNodeKey(String key) {
		this.nodeKey = key;
	}

	public void addToSuccs(Node node) {
		if(!node.getNodeKey().equals(this.getNodeKey()))
			this.succs.put(node.getNodeKey(), node);
	}

	public void removeFromSuccs(Node node) {
		this.succs.remove(node.getNodeKey());
	}

	public void copyToSuccs(Node n) {
		for (Node currentSucc : n.getSuccs().values()) {
			this.addToSuccs(currentSucc);
			currentSucc.addToSuccs(this);
		}
	}

	public void clearSuccs() {
		this.succs.clear();
	}

	public void addToPrdcs(Node node) {
		if(!node.getNodeKey().equals(this.getNodeKey()))
			this.prdcs.put(node.getNodeKey(), node);
	}

	public void removeFromPrdcs(Node node) {
		this.prdcs.remove(node.getNodeKey());
	}

	public int getInDegree() {
		return this.prdcs.size();
	}

	public int getOutDegree() {
		return this.succs.size();
	}

	public int getTotalDegree() {
		return (getInDegree() + getOutDegree());
	}


	public boolean succsContains(String key) {
		if(succs.containsKey(key))
			return true;
		return false;
	}

	public HashMap<String,Node> getSuccs() {
		return this.succs;
	}

	public HashMap<String,Node> getPrdcs() {
		return this.prdcs;
	}

	public boolean prdcsContains(String key) {
		if(prdcs.containsKey(key))
			return true;
		return false;
	}


	public boolean isAdjacent(String key) {
		return succs.containsKey(key) || prdcs.containsKey(key);
	}

	public boolean equals(Object obj) {
		if(!(obj instanceof Node))
			return false;
		else {
			Node n = (Node) obj;
			return n.getNodeKey().equals(this.getNodeKey());
		}
	}

	public Object clone() {
		try {
			Node n = (Node) super.clone();
			n.succs = (HashMap<String, Node>) this.succs.clone();
			n.prdcs = (HashMap<String, Node>) this.prdcs.clone();
			return n;
		} catch (Exception e) {
			throw new InternalError(e.toString());
		}
	}

	public String toString() {
		return this.nodeKey;
	}
}
